﻿using HtmlAgilityPack;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace video
{
    class Video
    {
        public static async Task<bool> Download(string url, string savePath)
        {
            // 创建路径
            if (!Directory.Exists(savePath))
            {
                DirectoryInfo directoryInfo = Directory.CreateDirectory(savePath);
                if (!directoryInfo.Exists)
                {
                    throw (new Exception(String.Format("创建文件夹失败：{0}", savePath)));
                }
            }

            if (url.Contains("douyin")) // https://v.douyin.com/3Lnb8j/
            {
                return await Video.DownloadDouYinVideo(url, savePath);
            }
            else if (url.Contains("kuaishou")) // https://f.kuaishou.com/1CYsQT
            {
                return await Video.DownloadKuaiShouVideo(url, savePath);
            }
            return false;
        }

        /// <summary>
        /// 下载快手视频
        /// </summary>
        /// <param name="url"></param>
        /// <param name="savePath"></param>
        /// <returns></returns>
        public static async Task<bool> DownloadKuaiShouVideo(string url, string savePath)
        {
            // 设置请求头
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                { "accept", "text/html" },
                { "accept-encoding", "gzip, deflate, br" },
                { "accept-language", "zh-CN" },
                { "cache-control", "no-cache" },
                { "pragma", "no-cache" },
                { "user-agent", "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1" }
            };
            // 获取Stream，存入文件
            HttpResponseMessage httpResponseMessage = await Request.Get(url, headers);
            Stream stream = new System.IO.Compression.GZipStream(await httpResponseMessage.Content.ReadAsStreamAsync(), System.IO.Compression.CompressionMode.Decompress);

            byte[] buffer = new byte[1024];
            Random random = new Random();
            string timeStamp = Tool.GetTimeStamp();
            string fileName = String.Format("kuaishou_{0}_{1}.html", timeStamp, random.Next(Convert.ToInt32(timeStamp)));
            string filePath = savePath + "/" + fileName;
            string readString = "";
            int actualBufferLength;

            // 存入临时文件
            while (stream.Read(buffer, 0, buffer.Length) > 0)
            {
                actualBufferLength = Tool.GetByteArrayLength(buffer);
                using (FileStream fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                {
                    fileStream.Write(buffer, 0, actualBufferLength);
                    readString += Encoding.UTF8.GetString(buffer, 0, actualBufferLength);
                }
                Array.Clear(buffer, 0, buffer.Length);
            }
            if (!File.Exists(filePath))
            {
                throw (new Exception(String.Format("创建临时文件失败：{0}", filePath)));
            }

            // 解析字符串
            HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
            htmlDocument.LoadHtml(readString);
            string scriptString = htmlDocument.DocumentNode.SelectSingleNode("//*[@data-pagedata]").Attributes["data-pagedata"].Value;
            scriptString = System.Net.WebUtility.HtmlDecode(scriptString);
            Dictionary<string, object> jsonDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(scriptString);
            jsonDict.TryGetValue("video", out object video);
            Dictionary<string, string> videoDict = JsonConvert.DeserializeObject<Dictionary<string, string>> (JsonConvert.SerializeObject(video));
            videoDict.TryGetValue("srcNoMark", out string videoUrl);
            Dictionary<string, string> mobileHeaders = new Dictionary<string, string>
            {
                //{ "user-agent", String.Format("Mobile/{0}", timeStamp) }
                { "user-agent", "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1" }
            };
            filePath = filePath.Replace(".html", ".mp4");
            if (await DownloadVideo(filePath, videoUrl, mobileHeaders) && File.Exists(filePath))
            {
                File.Delete(filePath.Replace(".mp4", ".html"));
                return true;
            }
            return false;
        }

        /// <summary>
        /// 下载抖音视频
        /// </summary>
        /// <param name="url"></param>
        /// <param name="savePath"></param>
        /// <returns></returns>
        public static async Task<bool> DownloadDouYinVideo(string url, string savePath)
        {
            
            // 设置请求头
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                { "accept", "text/html" },
                { "accept-encoding", "gzip, deflate, br" },
                { "accept-language", "zh-CN" },
                { "cache-control", "no-cache" },
                { "pragma", "no-cache" },
                { "user-agent", "Chrome/80.0.3987.122" }
            };

            // 获取Stream，存入文件
            HttpResponseMessage httpResponseMessage = await Request.Get(url, headers);
            Stream stream = new System.IO.Compression.GZipStream(await httpResponseMessage.Content.ReadAsStreamAsync(), System.IO.Compression.CompressionMode.Decompress);

            byte[] buffer = new byte[1024];
            Random random = new Random();
            string timeStamp = Tool.GetTimeStamp();
            string fileName = String.Format("douyin_{0}_{1}.html", timeStamp, random.Next(Convert.ToInt32(timeStamp)));
            string filePath = savePath + "/" + fileName;
            string readString = "";
            int actualBufferLength;

            // 存入临时文件
            while (stream.Read(buffer, 0, buffer.Length) > 0)
            {
                actualBufferLength = Tool.GetByteArrayLength(buffer);
                using (FileStream fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                {
                    fileStream.Write(buffer, 0, actualBufferLength);
                    readString += Encoding.UTF8.GetString(buffer, 0, actualBufferLength);
                }
                Array.Clear(buffer, 0, buffer.Length);
            }
            if (!File.Exists(filePath))
            {
                throw (new Exception(String.Format("创建临时文件失败：{0}", filePath)));
            }

            // 解析字符串
            HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
            htmlDocument.LoadHtml(readString);
            string scriptString = htmlDocument.DocumentNode.SelectSingleNode("/html/body/div/script[3]").OuterHtml;
            string startString = "playAddr: \"";
            int startIndex = scriptString.IndexOf(startString) + startString.Length;
            int endIndex = scriptString.LastIndexOf("\",");
            scriptString = scriptString.Substring(startIndex, endIndex - startIndex);
            string videoUrl = scriptString.Replace("playwm", "play");

            Dictionary<string, string> mobileHeaders = new Dictionary<string, string>
            {
                //{ "user-agent", String.Format("Mobile/{0}", timeStamp) }
                { "user-agent", "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1" }
            };

            // 下载视频，存入临时文件
            httpResponseMessage = await Request.Get(videoUrl, mobileHeaders);
            httpResponseMessage.EnsureSuccessStatusCode();
            filePath = filePath.Replace(".html", ".mp4");
            if (await DownloadVideo(filePath, videoUrl, mobileHeaders) && File.Exists(filePath))
            {
                File.Delete(filePath.Replace(".mp4", ".html"));
                return true;
            }
            return false;

        }

        public static async Task<bool> DownloadVideo(string filePath, string videoUrl, Dictionary<string, string> headers)
        {
            byte[] buffer = new byte[1024];

            HttpResponseMessage httpResponseMessage = await Request.Get(videoUrl, headers);
            httpResponseMessage.EnsureSuccessStatusCode();
            Stream videoStream = await httpResponseMessage.Content.ReadAsStreamAsync();
            while (videoStream.Read(buffer, 0, buffer.Length) > 0)
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                {
                    fileStream.Write(buffer, 0, buffer.Length);
                }
                Array.Clear(buffer, 0, buffer.Length);
            }
            if (File.Exists(filePath))
            {
                File.Delete(filePath.Replace(".mp4", ".html"));
                return true;
            }
            return false;
        }
    }
}
